#ifndef Q_MOC_RUN
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#endif

#include "BoostLogger.h"

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(Boost_Logger, boost::log::sources::severity_channel_logger_mt<log_levels>)

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", log_levels)

struct severity_tag;

boost::log::formatting_ostream& operator<< (
        boost::log::formatting_ostream& strm,
        boost::log::to_log_manip<log_levels, severity_tag > const& manip)
{
    static const int NUM_SEVERITY_LEVELS = 9;
    static const char* severity_level_str[] =
    {
//        "COM",
//        "HWD",
        "DBG",
        "TRC",
        "INF",
        "WRN",
        "ERR",
        "FTL",
        "FIX"
    };

    log_levels lvl = manip.get();
    const char* str = severity_level_str[lvl];
    if (lvl < NUM_SEVERITY_LEVELS && lvl >= 0)
        strm << str;
    else
        strm << static_cast< int >(lvl);
    return strm;
}

BoostLogger::BoostLogger(log_levels level)
{
    _defaultPrefix = "";
    _logLevel = level;
}

void BoostLogger::Initialize()
{
    boost::log::register_simple_formatter_factory<log_levels, char>("Severity");
}

void BoostLogger::Log(log_levels level, const std::string &information)
{
    BOOST_LOG_CHANNEL_SEV(Boost_Logger::get(), _defaultPrefix, level) << information;
}

void BoostLogger::Log(log_levels level, const std::string &information, const std::string & prefix)
{
    BOOST_LOG_CHANNEL_SEV(Boost_Logger::get(), prefix, level) << information;
}

bool BoostLogger::AddLogFilter(const std::string &folder, const std::string & prefix, bool isDefault)
{
    std::string fileName = folder + "\\" + prefix + "_%Y%m%d_%H%M%S_%5N.log";
    std::string archiveFolder = folder + "\\Archive";
    typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend> file_sink;
    boost::shared_ptr<file_sink> sink(new file_sink(
        // file name pattern
        boost::log::keywords::file_name = fileName.c_str(),
        // rotation size, in characters
        boost::log::keywords::rotation_size = 5 * 1024 * 1024,
        // make each log record flushed to the file
        boost::log::keywords::auto_flush = true
        ));

    // Set filter to prefix.
    sink->set_filter(severity >= _logLevel && boost::log::expressions::attr<std::string>("Channel") == prefix);

    // Set up where the rotated files will be stored
    sink->locked_backend()->set_file_collector(boost::log::sinks::file::make_collector(
        // where to store rotated files
        boost::log::keywords::target = archiveFolder.c_str(),
        // oldest log files will be removed if the total size reaches 100 MiB...
        boost::log::keywords::max_size = 1000 * 1024 * 1024,
        // ...or the free space in the target directory comes down to 50 MiB
        boost::log::keywords::min_free_space = 50 * 1024 * 1024
        ));

    if (isDefault)
    {
        // Upon restart, scan the target directory for files matching the file_name pattern
        sink->locked_backend()->scan_for_files();
    }

    // Set log record format.
    sink->set_formatter(
        boost::log::expressions::stream
                << "[" << boost::log::expressions::attr<boost::log::attributes::current_thread_id::value_type>("ThreadID")
                << "][" << boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
                << "] <" << boost::log::expressions::attr<log_levels, severity_tag>("Severity") << "> "
                << boost::log::expressions::smessage
        );

    // Add it to the core
    boost::log::core::get()->add_sink(sink);

    if (isDefault || _defaultPrefix.empty())
    {
        // Add some attributes
        boost::log::add_common_attributes();

        _defaultPrefix = prefix;
    }

    return true;
}
